home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / spamexperts / OptionsClass.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  27.5 KB  |  914 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''OptionsClass
  5.  
  6. Classes:
  7.     Option - Holds information about an option
  8.     OptionsClass - A collection of options
  9.  
  10. Abstract:
  11.  
  12. This module is used to manage "options" managed in user editable files.
  13. This is the implementation of the Options.options globally shared options
  14. object for the SpamBayes project, but is also able to be used to manage
  15. other options required by each application.
  16.  
  17. The Option class holds information about an option - the name of the
  18. option, a nice name (to display), documentation, default value,
  19. possible values (a tuple or a regex pattern), whether multiple values
  20. are allowed, and whether the option should be reset when restoring to
  21. defaults (options like server names should *not* be).
  22.  
  23. The OptionsClass class provides facility for a collection of Options.
  24. It is expected that manipulation of the options will be carried out
  25. via an instance of this class.
  26.  
  27. Experimental or deprecated options are prefixed with \'x-\', borrowing the
  28. practice from RFC-822 mail.  If the user sets an option like:
  29.  
  30.     [Tokenizer]
  31.     x-transmogrify: True
  32.  
  33. and an \'x-transmogrify\' or \'transmogrify\' option exists, it is set silently
  34. to the value given by the user.  If the user sets an option like:
  35.  
  36.     [Tokenizer]
  37.     transmogrify: True
  38.  
  39. and no \'transmogrify\' option exists, but an \'x-transmogrify\' option does,
  40. the latter is set to the value given by the users and a deprecation message
  41. is printed to standard error.
  42.  
  43. To Do:
  44.  o Stop allowing invalid options in configuration files
  45.  o str(Option) should really call Option.unconvert since this is what
  46.    it does.  Try putting that in and running all the tests.
  47.  o [See also the __issues__ string.]
  48. '''
  49. __credits__ = 'All the Spambayes folk.'
  50. __issues__ = 'Things that should be considered further and by\nother people:\n\nWe are very generous in checking validity when multiple values are\nallowed and the check is a regex (rather than a tuple).  Any sequence\nthat does not match the regex may be used to delimit the values.\nFor example, if the regex was simply r"[\\d]*" then these would all\nbe considered valid:\n"123a234" -> 123, 234\n"123abced234" -> 123, 234\n"123XST234xas" -> 123, 234\n"123 234" -> 123, 234\n"123~!@$%^&@234!" -> 123, 234\n\nIf this is a problem, my recommendation would be to change the\nmultiple_values_allowed attribute from a boolean to a regex/None\ni.e. if multiple is None, then only one value is allowed.  Otherwise\nmultiple is used in a re.split() to separate the input.\n'
  51. import sys
  52. import os
  53. import shutil
  54. from tempfile import TemporaryFile
  55.  
  56. try:
  57.     import cStringIO as StringIO
  58. except ImportError:
  59.     import StringIO
  60.  
  61. import re
  62. import types
  63. import locale
  64.  
  65. try:
  66.     (True, False, bool)
  67. except NameError:
  68.     (True, False) = (1, 0)
  69.     
  70.     def bool(val):
  71.         return not (not val)
  72.  
  73.  
  74.  
  75. try:
  76.     import textwrap
  77.     raise ImportError
  78. except ImportError:
  79.     
  80.     def wrap(s):
  81.         length = 10
  82.         return [ s[i:i + length].strip() for i in xrange(0, len(s), length) ]
  83.  
  84.  
  85. wrap = textwrap.wrap
  86. __all__ = [
  87.     'OptionsClass',
  88.     'SEOptionsClass',
  89.     'HEADER_NAME',
  90.     'HEADER_VALUE',
  91.     'INTEGER',
  92.     'REAL',
  93.     'BOOLEAN',
  94.     'SERVER',
  95.     'PORT',
  96.     'EMAIL_ADDRESS',
  97.     'PATH',
  98.     'VARIABLE_PATH',
  99.     'FILE',
  100.     'FILE_WITH_PATH',
  101.     'IMAP_FOLDER',
  102.     'IMAP_ASTRING',
  103.     'RESTORE',
  104.     'DO_NOT_RESTORE',
  105.     'IP_LIST',
  106.     'OFF',
  107.     'ON',
  108.     'BLOCK_SPAM',
  109.     'MODIFY_HEADER',
  110.     'MODIFY_SUBJECT',
  111.     'IS_HAM',
  112.     'IS_SPAM',
  113.     'IS_SPAM',
  114.     'IS_UNKNOWN',
  115.     'WHITELIST',
  116.     'BLACKLIST',
  117.     'IM_FROM',
  118.     'IM_SUBJECT',
  119.     'IM_BODY',
  120.     'BLOCKED',
  121.     'DELAYED',
  122.     'REMOVED',
  123.     'FORWARD',
  124.     'DELETE']
  125. MultiContainerTypes = (types.TupleType, types.ListType)
  126.  
  127. class Option(object):
  128.     
  129.     def __init__(self, name, nice_name = '', default = None, help_text = '', allowed = None, restore = True):
  130.         self.name = name
  131.         self.nice_name = nice_name
  132.         self.default_value = default
  133.         self.explanation_text = help_text
  134.         self.allowed_values = allowed
  135.         self.restore = restore
  136.         self.delimiter = None
  137.         self.set(default)
  138.  
  139.     
  140.     def display_name(self):
  141.         '''A name for the option suitable for display to a user.'''
  142.         return self.nice_name
  143.  
  144.     
  145.     def default(self):
  146.         '''The default value for the option.'''
  147.         return self.default_value
  148.  
  149.     
  150.     def doc(self):
  151.         '''Documentation for the option.'''
  152.         return self.explanation_text
  153.  
  154.     
  155.     def valid_input(self):
  156.         '''Valid values for the option.'''
  157.         return self.allowed_values
  158.  
  159.     
  160.     def no_restore(self):
  161.         '''Do not restore this option when restoring to defaults.'''
  162.         return not (self.restore)
  163.  
  164.     
  165.     def set(self, val):
  166.         '''Set option to value.'''
  167.         self.value = val
  168.  
  169.     
  170.     def get(self):
  171.         '''Get option value.'''
  172.         return self.value
  173.  
  174.     
  175.     def multiple_values_allowed(self):
  176.         '''Multiple values are allowed for this option.'''
  177.         return type(self.default_value) in MultiContainerTypes
  178.  
  179.     
  180.     def is_valid(self, value):
  181.         '''Check if this is a valid value for this option.'''
  182.         if self.allowed_values is None:
  183.             return False
  184.         
  185.         if self.multiple_values_allowed():
  186.             return self.is_valid_multiple(value)
  187.         else:
  188.             return self.is_valid_single(value)
  189.  
  190.     
  191.     def is_valid_multiple(self, value):
  192.         '''Return True iff value is a valid value for this option.
  193.         Use if multiple values are allowed.'''
  194.         if type(value) in MultiContainerTypes:
  195.             for val in value:
  196.                 if not self.is_valid_single(val):
  197.                     return False
  198.                     continue
  199.             
  200.             return True
  201.         
  202.         return self.is_valid_single(value)
  203.  
  204.     
  205.     def is_valid_single(self, value):
  206.         '''Return True iff value is a valid value for this option.
  207.         Use when multiple values are not allowed.'''
  208.         if type(self.allowed_values) == types.TupleType:
  209.             if value in self.allowed_values:
  210.                 return True
  211.             else:
  212.                 return False
  213.         elif self.is_boolean:
  214.             if value == True or value == False:
  215.                 return True
  216.             
  217.         if type(value) != type(self.value) and type(self.value) not in MultiContainerTypes:
  218.             if type(value) not in types.StringTypes or type(self.value) not in types.StringTypes:
  219.                 return False
  220.             
  221.         
  222.         if value == '':
  223.             return True
  224.         
  225.         avals = self._split_values(value)
  226.         if len(avals) == 1:
  227.             return True
  228.         else:
  229.             return False
  230.  
  231.     
  232.     def _split_values(self, value):
  233.         if not self.allowed_values:
  234.             return ('',)
  235.         
  236.         
  237.         try:
  238.             r = re.compile(self.allowed_values)
  239.         except:
  240.             print >>sys.stderr, self.allowed_values
  241.             raise 
  242.  
  243.         s = str(value)
  244.         i = 0
  245.         vals = []
  246.         while True:
  247.             m = r.search(s[i:])
  248.             if m is None:
  249.                 break
  250.             
  251.             vals.append(m.group())
  252.             delimiter = s[i:i + m.start()]
  253.             if self.delimiter is None and delimiter != '':
  254.                 self.delimiter = delimiter
  255.             
  256.             i += m.end()
  257.         return tuple(vals)
  258.  
  259.     
  260.     def as_nice_string(self, section = None):
  261.         '''Summarise the option in a user-readable format.'''
  262.         if section is None:
  263.             strval = ''
  264.         else:
  265.             strval = '[%s] ' % section
  266.         strval += '%s - "%s"\nDefault: %s\nDo not restore: %s\n' % (self.name, self.display_name(), str(self.default()), str(self.no_restore()))
  267.         strval += 'Valid values: %s\nMultiple values allowed: %s\n' % (str(self.valid_input()), str(self.multiple_values_allowed()))
  268.         strval += '"%s"\n\n' % str(self.doc())
  269.         return strval
  270.  
  271.     
  272.     def as_documentation_string(self, section = None):
  273.         '''Summarise the option in a format suitable for unmodified
  274.         insertion in HTML documentation.'''
  275.         strval = [
  276.             '<tr>']
  277.         if section is not None:
  278.             strval.append('\t<td>[%s]</td>' % (section,))
  279.         
  280.         strval.append('\t<td>%s</td>' % (self.name,))
  281.         ', '.join([] % []([ str(s) for s in self.valid_input() ]))
  282.         default = self.default()
  283.         strval.append('\t<td>%s</td>' % (default,))
  284.         strval.append('\t<td><strong>%s</strong>: %s</td>' % (self.display_name(), self.doc()))
  285.         strval.append('</tr>\n')
  286.         return '\n'.join(strval)
  287.  
  288.     
  289.     def write_config(self, file):
  290.         '''Output value in configuration file format.'''
  291.         file.write(self.name)
  292.         file.write(': ')
  293.         file.write(self.unconvert())
  294.         file.write('\n')
  295.  
  296.     
  297.     def convert(self, value):
  298.         '''Convert value from a string to the appropriate type.'''
  299.         svt = type(self.value)
  300.         if svt == type(value):
  301.             return value
  302.         
  303.         if type(self.allowed_values) == types.TupleType and value in self.allowed_values:
  304.             return value
  305.         
  306.         if self.is_boolean():
  307.             if str(value) == 'True' or value == 1:
  308.                 return True
  309.             elif str(value) == 'False' or value == 0:
  310.                 return False
  311.             
  312.             raise TypeError, self.name + ' must be True or False'
  313.         
  314.         if self.multiple_values_allowed():
  315.             if isinstance(self.allowed_values, types.StringTypes):
  316.                 vals = list(self._split_values(value))
  317.             elif isinstance(value, types.TupleType):
  318.                 vals = list(value)
  319.             else:
  320.                 vals = value.split()
  321.             if len(self.default_value) > 0:
  322.                 to_type = type(self.default_value[0])
  323.             else:
  324.                 to_type = types.StringType
  325.             for i in range(0, len(vals)):
  326.                 vals[i] = self._convert(vals[i], to_type)
  327.             
  328.             return tuple(vals)
  329.         else:
  330.             return self._convert(value, svt)
  331.         raise TypeError, self.name + ' has an invalid type.'
  332.  
  333.     
  334.     def _convert(self, value, to_type):
  335.         '''Convert an int, float or string to the specified type.'''
  336.         if to_type == type(value):
  337.             return value
  338.         
  339.         if to_type == types.IntType:
  340.             return locale.atoi(value)
  341.         
  342.         if to_type == types.FloatType:
  343.             return locale.atof(value)
  344.         
  345.         if to_type in types.StringTypes:
  346.             return str(value)
  347.         
  348.         raise TypeError, 'Invalid type.'
  349.  
  350.     
  351.     def unconvert(self):
  352.         '''Convert value from the appropriate type to a string.'''
  353.         if type(self.value) in types.StringTypes:
  354.             return self.value
  355.         
  356.         if self.is_boolean():
  357.             if self.value == True:
  358.                 return 'True'
  359.             else:
  360.                 return 'False'
  361.         
  362.         if type(self.value) == types.TupleType:
  363.             if len(self.value) == 0:
  364.                 return ''
  365.             
  366.             if len(self.value) == 1:
  367.                 v = self.value[0]
  368.                 if type(v) == types.FloatType:
  369.                     return locale.str(self.value[0])
  370.                 
  371.                 return str(v)
  372.             
  373.             strval = ''
  374.             if self.delimiter is None:
  375.                 if type(self.allowed_values) == types.TupleType:
  376.                     self.delimiter = ' '
  377.                 else:
  378.                     v0 = self.value[0]
  379.                     v1 = self.value[1]
  380.                     for sep in [
  381.                         ' ',
  382.                         ',',
  383.                         ':',
  384.                         ';',
  385.                         '/',
  386.                         '\\',
  387.                         None]:
  388.                         test_str = str(v0) + sep + str(v1)
  389.                         test_tuple = self._split_values(test_str)
  390.                         if test_tuple[0] == str(v0) and test_tuple[1] == str(v1) and len(test_tuple) == 2:
  391.                             break
  392.                             continue
  393.                     
  394.                     self.delimiter = sep
  395.             
  396.             for v in self.value:
  397.                 if type(v) == types.FloatType:
  398.                     v = locale.str(v)
  399.                 else:
  400.                     v = str(v)
  401.                 strval += v + self.delimiter
  402.             
  403.             strval = strval[:-len(self.delimiter)]
  404.         else:
  405.             strval = str(self.value)
  406.         return strval
  407.  
  408.     
  409.     def is_boolean(self):
  410.         '''Return True iff the option is a boolean value.'''
  411.         
  412.         try:
  413.             if type(self.allowed_values) == types.TupleType and len(self.allowed_values) > 0 and type(self.allowed_values[0]) == types.BooleanType:
  414.                 return True
  415.             
  416.             return False
  417.         except AttributeError:
  418.             if self.allowed_values == (False, True):
  419.                 return True
  420.             
  421.             return False
  422.  
  423.  
  424.  
  425.  
  426. class OptionsClass(object):
  427.     
  428.     def __init__(self):
  429.         self.verbose = None
  430.         self._options = { }
  431.         self.restore_point = { }
  432.         self.conversion_table = { }
  433.  
  434.     SECTCRE = re.compile('\\[(?P<header>[^]]+)\\]')
  435.     OPTCRE = re.compile('(?P<option>[^:=\\s][^:=]*)\\s*(?P<vi>[:=])\\s*(?P<value>.*)$')
  436.     
  437.     def update_file(self, filename):
  438.         '''Update the specified configuration file.'''
  439.         sectname = None
  440.         optname = None
  441.         out = TemporaryFile()
  442.         if os.path.exists(filename):
  443.             f = file(filename, 'r')
  444.         elif self.verbose:
  445.             print >>sys.stderr, 'Creating new configuration file',
  446.             print >>sys.stderr, filename
  447.         
  448.         f = file(filename, 'w')
  449.         f.close()
  450.         f = file(filename, 'r')
  451.         written = []
  452.         vi = ': '
  453.         while True:
  454.             line = f.readline()
  455.             if not line:
  456.                 break
  457.             
  458.             if line.strip() == '' or line[0] in '#;':
  459.                 out.write(line)
  460.                 continue
  461.             
  462.             if line.split(None, 1)[0].lower() == 'rem' and line[0] in 'rR':
  463.                 out.write(line)
  464.                 continue
  465.             
  466.             if line[0].isspace() and sectname is not None and optname:
  467.                 continue
  468.                 continue
  469.             mo = self.SECTCRE.match(line)
  470.             if mo:
  471.                 if sectname is not None:
  472.                     self._add_missing(out, written, sectname, vi, False)
  473.                 
  474.                 sectname = mo.group('header')
  475.                 optname = None
  476.                 if sectname in self.sections():
  477.                     out.write(line)
  478.                 
  479.             sectname in self.sections()
  480.             mo = self.OPTCRE.match(line)
  481.             if mo:
  482.                 (optname, vi, optval) = mo.group('option', 'vi', 'value')
  483.                 if vi in ('=', ':') and ';' in optval:
  484.                     pos = optval.find(';')
  485.                     if pos != -1 and optval[pos - 1].isspace():
  486.                         optval = optval[:pos]
  487.                     
  488.                 
  489.                 optval = optval.strip()
  490.                 if optval == '""':
  491.                     optval = ''
  492.                 
  493.                 optname = optname.rstrip().lower()
  494.                 if self._options.has_key((sectname, optname)):
  495.                     out.write(optname)
  496.                     out.write(vi)
  497.                     newval = self.unconvert(sectname, optname)
  498.                     out.write(newval.replace('\n', '\n\t'))
  499.                     out.write('\n')
  500.                     written.append((sectname, optname))
  501.                 
  502.             self._options.has_key((sectname, optname))
  503.         for sect in self.sections():
  504.             self._add_missing(out, written, sect, vi)
  505.         
  506.         f.close()
  507.         out.flush()
  508.         if self.verbose:
  509.             shutil.copyfile(filename, filename + '.bak')
  510.         
  511.         f = file(filename, 'w')
  512.         out.seek(0)
  513.         shutil.copyfileobj(out, f)
  514.         out.close()
  515.         f.close()
  516.  
  517.     
  518.     def _add_missing(self, out, written, sect, vi, label = True):
  519.         for opt in self.options_in_section(sect):
  520.             if (sect, opt) not in written and self.get(sect, opt) != self.default(sect, opt):
  521.                 if label:
  522.                     out.write('[')
  523.                     out.write(sect)
  524.                     out.write(']\n')
  525.                     label = False
  526.                 
  527.                 out.write(opt)
  528.                 out.write(vi)
  529.                 newval = self.unconvert(sect, opt)
  530.                 out.write(newval.replace('\n', '\n\t'))
  531.                 out.write('\n')
  532.                 written.append((sect, opt))
  533.                 continue
  534.         
  535.  
  536.     
  537.     def load_defaults(self, defaults):
  538.         '''Load default values (stored in Options.py).'''
  539.         for section, opts in defaults.items():
  540.             for opt in opts:
  541.                 klass = Option
  542.                 args = opt
  543.                 
  544.                 try:
  545.                     if issubclass(opt[0], Option):
  546.                         klass = opt[0]
  547.                         args = opt[1:]
  548.                 except TypeError:
  549.                     pass
  550.  
  551.                 o = klass(*args)
  552.                 self._options[(section, o.name)] = o
  553.             
  554.         
  555.  
  556.     
  557.     def set_restore_point(self):
  558.         '''Remember what the option values are right now, to
  559.         be able to go back to them, via revert_to_restore_point().
  560.  
  561.         Any existing restore point is wiped.  Restore points do
  562.         not persist over sessions.
  563.         '''
  564.         self.restore_point = { }
  565.         for key, opt_obj in self._options.iteritems():
  566.             self.restore_point[key] = opt_obj.get()
  567.         
  568.  
  569.     
  570.     def revert_to_restore_point(self):
  571.         '''Restore option values to their values when set_restore_point()
  572.         was last called.
  573.  
  574.         If set_restore_point() has not been called, then this has no
  575.         effect.  If new options have been added since set_restore_point,
  576.         their values are not effected.
  577.         '''
  578.         for key, value in self.restore_point.iteritems():
  579.             self._options[key].set(value)
  580.         
  581.  
  582.     
  583.     def merge_files(self, file_list):
  584.         for f in file_list:
  585.             self.merge_file(f)
  586.         
  587.  
  588.     
  589.     def convert_and_set(self, section, option, value):
  590.         value = self.convert(section, option, value)
  591.         self.set(section, option, value)
  592.  
  593.     
  594.     def merge_file(self, filename):
  595.         import ConfigParser
  596.         c = ConfigParser.ConfigParser()
  597.         c.read(filename)
  598.         for sect in c.sections():
  599.             for opt in c.options(sect):
  600.                 value = c.get(sect, opt)
  601.                 section = sect
  602.                 option = opt
  603.                 if not self._options.has_key((section, option)):
  604.                     pass
  605.                 None if option.startswith('x-') else self._options.has_key((section, option))
  606.                 self.convert_and_set(section, option, value)
  607.             
  608.         
  609.  
  610.     
  611.     def display_name(self, sect, opt):
  612.         '''A name for the option suitable for display to a user.'''
  613.         return self._options[(sect, opt.lower())].display_name()
  614.  
  615.     
  616.     def default(self, sect, opt):
  617.         '''The default value for the option.'''
  618.         return self._options[(sect, opt.lower())].default()
  619.  
  620.     
  621.     def doc(self, sect, opt):
  622.         '''Documentation for the option.'''
  623.         return self._options[(sect, opt.lower())].doc()
  624.  
  625.     
  626.     def valid_input(self, sect, opt):
  627.         '''Valid values for the option.'''
  628.         return self._options[(sect, opt.lower())].valid_input()
  629.  
  630.     
  631.     def no_restore(self, sect, opt):
  632.         '''Do not restore this option when restoring to defaults.'''
  633.         return self._options[(sect, opt.lower())].no_restore()
  634.  
  635.     
  636.     def is_valid(self, sect, opt, value):
  637.         '''Check if this is a valid value for this option.'''
  638.         return self._options[(sect, opt.lower())].is_valid(value)
  639.  
  640.     
  641.     def multiple_values_allowed(self, sect, opt):
  642.         '''Multiple values are allowed for this option.'''
  643.         return self._options[(sect, opt.lower())].multiple_values_allowed()
  644.  
  645.     
  646.     def is_boolean(self, sect, opt):
  647.         '''The option is a boolean value. (Support for Python 2.2).'''
  648.         return self._options[(sect, opt.lower())].is_boolean()
  649.  
  650.     
  651.     def convert(self, sect, opt, value):
  652.         '''Convert value from a string to the appropriate type.'''
  653.         return self._options[(sect, opt.lower())].convert(value)
  654.  
  655.     
  656.     def unconvert(self, sect, opt):
  657.         '''Convert value from the appropriate type to a string.'''
  658.         return self._options[(sect, opt.lower())].unconvert()
  659.  
  660.     
  661.     def get_option(self, sect, opt):
  662.         '''Get an option.'''
  663.         if self.conversion_table.has_key((sect, opt)):
  664.             (sect, opt) = self.conversion_table[(sect, opt)]
  665.         
  666.         return self._options[(sect, opt.lower())]
  667.  
  668.     
  669.     def get(self, sect, opt):
  670.         '''Get an option value.'''
  671.         if self.conversion_table.has_key((sect, opt.lower())):
  672.             (sect, opt) = self.conversion_table[(sect, opt.lower())]
  673.         
  674.         return self.get_option(sect, opt.lower()).get()
  675.  
  676.     
  677.     def __getitem__(self, key):
  678.         return self.get(key[0], key[1])
  679.  
  680.     
  681.     def set(self, sect, opt, val = None):
  682.         '''Set an option.'''
  683.         if self.conversion_table.has_key((sect, opt.lower())):
  684.             (sect, opt) = self.conversion_table[(sect, opt.lower())]
  685.         
  686.         if sect == 'Headers' and opt in ('notate_to', 'notate_subject'):
  687.             self._options[(sect, opt.lower())].set(val)
  688.             return None
  689.         
  690.         if self.is_valid(sect, opt, val):
  691.             self._options[(sect, opt.lower())].set(val)
  692.         else:
  693.             print >>sys.stderr, 'Attempted to set [%s] %s with invalid value %s (%s)' % (sect, opt.lower(), val, type(val))
  694.  
  695.     
  696.     def set_from_cmdline(self, arg, stream = None):
  697.         '''Set option from colon-separated sect:opt:val string.
  698.  
  699.         If optional stream arg is not None, error messages will be displayed
  700.         on stream, otherwise KeyErrors will be propagated up the call chain.
  701.         '''
  702.         (sect, opt, val) = arg.split(':', 2)
  703.         opt = opt.lower()
  704.         
  705.         try:
  706.             val = self.convert(sect, opt, val)
  707.         except (KeyError, TypeError):
  708.             msg = None
  709.             if stream is not None:
  710.                 self._report_option_error(sect, opt, val, stream, msg)
  711.             else:
  712.                 raise 
  713.         except:
  714.             stream is not None
  715.  
  716.         self.set(sect, opt, val)
  717.  
  718.     
  719.     def _report_deprecated_error(self, sect, opt):
  720.         print >>sys.stderr, 'Warning: option %s in section %s is deprecated' % (opt, sect)
  721.  
  722.     
  723.     def _report_option_error(self, sect, opt, val, stream, msg):
  724.         if sect in self.sections():
  725.             vopts = self.options(True)
  726.             vopts = _[1]
  727.             if opt not in vopts:
  728.                 print >>stream, 'Invalid option:', opt
  729.                 print >>stream, 'Valid options for', sect, 'are:'
  730.                 vopts = ', '.join(vopts)
  731.                 vopts = wrap(vopts)
  732.                 for line in vopts:
  733.                     print >>stream, '  ', line
  734.                 
  735.             else:
  736.                 print >>stream, 'Invalid value:', msg
  737.         else:
  738.             print >>stream, 'Invalid section:', sect
  739.             print >>stream, 'Valid sections are:'
  740.             vsects = ', '.join(self.sections())
  741.             vsects = wrap(vsects)
  742.             for line in vsects:
  743.                 print >>stream, '  ', line
  744.             
  745.  
  746.     
  747.     def __setitem__(self, key, value):
  748.         self.set(key[0], key[1], value)
  749.  
  750.     
  751.     def sections(self):
  752.         '''Return an alphabetical list of all the sections.'''
  753.         all = []
  754.         for sect, opt in self._options.keys():
  755.             if sect not in all:
  756.                 all.append(sect)
  757.                 continue
  758.         
  759.         all.sort()
  760.         return all
  761.  
  762.     
  763.     def options_in_section(self, section):
  764.         '''Return an alphabetical list of all the options in this section.'''
  765.         all = []
  766.         for sect, opt in self._options.keys():
  767.             if sect == section:
  768.                 all.append(opt)
  769.                 continue
  770.         
  771.         all.sort()
  772.         return all
  773.  
  774.     
  775.     def options(self, prepend_section_name = False):
  776.         '''Return an alphabetical list of all the options, optionally
  777.         prefixed with [section_name]'''
  778.         all = []
  779.         for sect, opt in self._options.keys():
  780.             if prepend_section_name:
  781.                 all.append('[' + sect + ']' + opt)
  782.                 continue
  783.             all.append(opt)
  784.         
  785.         all.sort()
  786.         return all
  787.  
  788.     
  789.     def display(self, add_comments = False):
  790.         '''Display options in a config file form.'''
  791.         output = StringIO.StringIO()
  792.         keys = self._options.keys()
  793.         keys.sort()
  794.         currentSection = None
  795.         for sect, opt in keys:
  796.             if sect != currentSection:
  797.                 if currentSection is not None:
  798.                     output.write('\n')
  799.                 
  800.                 output.write('[')
  801.                 output.write(sect)
  802.                 output.write(']\n')
  803.                 currentSection = sect
  804.             
  805.             if add_comments:
  806.                 doc = self._options[(sect, opt)].doc()
  807.                 if not doc:
  808.                     doc = 'No information available, sorry.'
  809.                 
  810.                 doc = re.sub('\\s+', ' ', doc)
  811.                 output.write('\n# %s\n' % ('\n# '.join(wrap(doc)),))
  812.             
  813.             self._options[(sect, opt)].write_config(output)
  814.         
  815.         return output.getvalue()
  816.  
  817.     
  818.     def _display_nice(self, section, option, formatter):
  819.         '''Display a nice output of the options'''
  820.         output = StringIO.StringIO()
  821.         if section is not None and option is not None:
  822.             opt = self._options[(section, option.lower())]
  823.             output.write(getattr(opt, formatter)(section))
  824.             return output.getvalue()
  825.         
  826.         all = self._options.keys()
  827.         all.sort()
  828.         for sect, opt in all:
  829.             if section is not None and sect != section:
  830.                 continue
  831.             
  832.             opt = self._options[(sect, opt.lower())]
  833.             output.write(getattr(opt, formatter)(sect))
  834.         
  835.         return output.getvalue()
  836.  
  837.     
  838.     def display_full(self, section = None, option = None):
  839.         '''Display options including all information.'''
  840.         return self._display_nice(section, option, 'as_nice_string')
  841.  
  842.     
  843.     def output_for_docs(self, section = None, option = None):
  844.         '''Return output suitable for inserting into documentation for
  845.         the available options.'''
  846.         return self._display_nice(section, option, 'as_documentation_string')
  847.  
  848.  
  849.  
  850. class SEOptionsClass(OptionsClass):
  851.     
  852.     def __init__(self, sb_options = None):
  853.         self.sb_options = sb_options
  854.         OptionsClass.__init__(self)
  855.  
  856.     
  857.     def set(self, sect, opt, val = None):
  858.         if self.sb_options and sect in self.sb_options.sections() and opt in self.sb_options.options_in_section(sect):
  859.             self.sb_options.set(sect, opt, val)
  860.         
  861.         OptionsClass.set(self, sect, opt, val)
  862.  
  863.  
  864. HEADER_NAME = '[\\w\\.\\-\\*]+'
  865. HEADER_VALUE = '.+'
  866. INTEGER = '[\\d]+'
  867. REAL = '[\\d]+[\\.]?[\\d]*'
  868. BOOLEAN = (False, True)
  869. SERVER = '([\\w\\.\\-]+(:[\\d]+)?)'
  870. PORT = '[\\d]+'
  871. EMAIL_ADDRESS = '[\\w\\-\\.]+@[\\w\\-\\.]+'
  872. PATH = '[\\w \\$\\.\\-~:\\\\/\\*\\@\\=]+'
  873. VARIABLE_PATH = PATH + '%'
  874. FILE = '[\\S]+'
  875. FILE_WITH_PATH = PATH
  876. IP_LIST = '\\*|localhost|((\\*|[01]?\\d\\d?|2[04]\\d|25[0-5])\\.(\\*|[01]?\\d\\d?|2[04]\\d|25[0-5])\\.(\\*|[01]?\\d\\d?|2[04]\\d|25[0-5])\\.(\\*|[01]?\\d\\d?|2[04]\\d|25[0-5]),?)+'
  877. IMAP_FOLDER = '[^,]+'
  878. IMAP_ASTRING = []
  879. for i in range(1, 128):
  880.     if not (chr(i) in [
  881.         '"',
  882.         '\\',
  883.         '\n',
  884.         '\r']):
  885.         IMAP_ASTRING.append(chr(i))
  886.     
  887.  
  888. IMAP_ASTRING = '\\"?[' + re.escape(''.join(IMAP_ASTRING)) + ']+\\"?'
  889. RESTORE = True
  890. DO_NOT_RESTORE = False
  891. OFF = 0
  892. ON = 1
  893. BLOCK_SPAM = 1
  894. MODIFY_HEADER = 2
  895. MODIFY_SUBJECT = 3
  896. IS_HAM = 'h'
  897. IS_SPAM = 's'
  898. IS_UNKNOWN = 'unknown'
  899. IS_UNSURE = 'unsure'
  900. WHITELIST_SENDER = 1
  901. WHITELIST_RECIPIENT = 2
  902. BLACKLIST_SENDER = 3
  903. BLACKLIST_RECIPIENT = 4
  904. WHITELIST = 5
  905. BLACKLIST = 6
  906. BLOCKED = 'b'
  907. DELAYED = 'd'
  908. FORWARD = 'f'
  909. REMOVED = 'r'
  910. DELETE = 's'
  911. IM_FROM = '"SpamExperts" <no-reply@localhost>'
  912. IM_SUBJECT = 'Intercepted Message'
  913. IM_BODY = '\nYou can find this intercepted message in the SpamExperts Spam or Unsure mailbox.\n'
  914.